package org.epam.szhuk.greenway.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;

import org.epam.szhuk.greenway.model.StoredGeoPoint;

import android.location.Location;
import android.util.Log;

public class TextParcer {
	private final String TAG = "TextParcer";
	public final static int DIVISION_RECORDS_MS_MIN_TIME=1000;

	public List<StoredGeoPoint> parseFile(String filePath/*, long startVideoTime, long duration*/) {
		Log.i(TAG, "Parsing text of file "+filePath+" started");
		File file = new File(filePath);
		Scanner scanner;
		List<StoredGeoPoint> storedGeoPointList=null;
		if((file!=null)&&(file.exists())){
		try {
			scanner = new Scanner(new FileReader(file));
			try {
				storedGeoPointList=new ArrayList<StoredGeoPoint>();				
				if(scanner.hasNextLine()){ //first record in list - check if time displacement needed for video from begin
					/*StoredGeoPoint tmpStoredGeoPoint=processFirstLine(scanner.nextLine(), startVideoTime, startVideoTime+duration);
					if(tmpStoredGeoPoint!=null){
						if(tmpStoredGeoPoint.getPointType()==StoredGeoPoint.POINT_TYPE_EMPTY){ //make 2 points from one - first with time displ value, second - usual
							StoredGeoPoint tmpStoredGeoPoint0=new StoredGeoPoint(tmpStoredGeoPoint.getLat(),tmpStoredGeoPoint.getLon(),startVideoTime,tmpStoredGeoPoint.getSpeed(),StoredGeoPoint.POINT_TYPE_EMPTY);
							storedGeoPointList.add(tmpStoredGeoPoint0);
							tmpStoredGeoPoint.setType(StoredGeoPoint.POINT_TYPE_USUAL);
						}					
						storedGeoPointList.add(tmpStoredGeoPoint);					
					}
					*/
				}
				while (scanner.hasNextLine()) {
					StoredGeoPoint tmpStoredGeoData=processLine(scanner.nextLine()/*, startTime, startTime+duration*/);
					if(tmpStoredGeoData!=null){
						if(tmpStoredGeoData.getTime()==-1){ //diapasone finished
							break;
						}
						else{
							storedGeoPointList.add(tmpStoredGeoData);
						}
					}
				}
				/*if(storedGeoPointList.size()>0){ //last record in list - check if time displacement needed for video till the end - add special marker
					StoredGeoPoint lastGP = storedGeoPointList.get(storedGeoPointList.size()-1);
					long lastCoordTime=lastGP.getTime();
					long endVideoTime=startVideoTime+duration;
					// TODO check if we need it here
					if((endVideoTime-lastCoordTime)>GeoDataProcessor.TIME_MS_ROBUST_SAME_TIME){
						StoredGeoPoint tmpStoredGeoPoint0=new StoredGeoPoint(lastGP.getLat(),lastGP.getLon(),endVideoTime,lastGP.getSpeed(),StoredGeoPoint.POINT_TYPE_EMPTY);
						storedGeoPointList.add(tmpStoredGeoPoint0);
					}
				} */
				
			}catch(IllegalStateException e){
					Log.e(TAG, "IllegalStateException: " , e);
				
			} finally {
				scanner.close();
			}
		} catch (FileNotFoundException e) {
			Log.e(TAG, "FileNotFoundException: ",e);
		} 
		Log.i(TAG, "Parsing text of file "+filePath+" finished");
		}
		else{
			Log.e(TAG, file.getAbsolutePath() + " not found or is null");
		}
		
		if(storedGeoPointList!=null){
			List<StoredGeoPoint> storedGeoPointListTotal=makeDivisionForSmallParts(storedGeoPointList);
			return storedGeoPointListTotal;
		}
		else{
			return null;
		}
		
	}

	/**
	 * To divide to smaller parts list of coord/speed
	 * 
	 * @param storedGPList - by business rules could have POINT_TYPE_EMPTY only in the begin and end of list
	 * @return
	 */
	private List<StoredGeoPoint> makeDivisionForSmallParts(List<StoredGeoPoint> storedGPList){
		List<StoredGeoPoint> storedGPListCropped=new ArrayList<StoredGeoPoint>();
		for(int i=0; i<storedGPList.size()-1; i++){ //chech for first and not-last coordinates
			long deltaTime=storedGPList.get(i+1).getTime()-storedGPList.get(i).getTime();
			if(deltaTime>DIVISION_RECORDS_MS_MIN_TIME){
				int numOfSteps=(int)(deltaTime/DIVISION_RECORDS_MS_MIN_TIME);
				StoredGeoPoint StoredGP0=storedGPList.get(i);
				StoredGeoPoint StoredGP1=storedGPList.get(i+1);
				storedGPListCropped.add(StoredGP0);				
				if((StoredGP0.getPointType()==StoredGeoPoint.POINT_TYPE_USUAL)&&(StoredGP1.getPointType()!=StoredGeoPoint.POINT_TYPE_EMPTY)){ // if usual coordinates and StoredGP0 is not pre-last coord when last is empty - approximate values of speed&coordinates
					int dLat= (StoredGP1.getLat()-StoredGP0.getLat())/numOfSteps; 
					int dLon=(StoredGP1.getLon()-StoredGP0.getLon())/numOfSteps;
					float[] distanceMeters=new float[1];//[0] is distance
					
					Location.distanceBetween(StoredGP0.getLat()/1E6, StoredGP0.getLon()/1E6, StoredGP1.getLat()/1E6, StoredGP1.getLon()/1E6, distanceMeters);
					float aproxSpeed=distanceMeters[0]/(deltaTime/1000);	
					for(int j=1; j<(numOfSteps); j++){ //j=1 because the 0-point been already added; numOfSteps-1  -- because the last point will be added as separate point
						int currentDLat=dLat*j;
						int currentDLon=dLon*j;
						int currentDTime=DIVISION_RECORDS_MS_MIN_TIME*j;
						
						StoredGeoPoint tmpStoredGP=new StoredGeoPoint(StoredGP0.getLat()+currentDLat, StoredGP0.getLon()+currentDLon,
								StoredGP0.getTime()+currentDTime, aproxSpeed,StoredGeoPoint.POINT_TYPE_APPROXIMATED); 
						//TODO: add proper speed calculation for approximated 
						storedGPListCropped.add(tmpStoredGP);
					}
					//storedGPListCropped.add(StoredGP1); //last point as  point - will be added in the next iteration
				}
				else if((StoredGP0.getPointType()==StoredGeoPoint.POINT_TYPE_EMPTY)||(StoredGP1.getPointType()==StoredGeoPoint.POINT_TYPE_EMPTY)){ // if empty coordinates 0 with time space or last coord is empty with time delay - just make more such coord
					for(int j=1; j<(numOfSteps); j++){ //j=1 because the 0-point been already added; numOfSteps-1  -- because the last point will be added as point
						int currentDTime=DIVISION_RECORDS_MS_MIN_TIME*j;
						StoredGeoPoint tmpStoredGP=new StoredGeoPoint(StoredGP0.getLat(), StoredGP0.getLon(),
								StoredGP0.getTime()+currentDTime, 0,StoredGeoPoint.POINT_TYPE_EMPTY);
						storedGPListCropped.add(tmpStoredGP);
					}
				} 
				
			}
			else{
				storedGPListCropped.add(storedGPList.get(i));
			}
		}
		storedGPListCropped.add(storedGPList.get(storedGPList.size()-1)); //adding the last coord
		Log.i(TAG,"storedGPListCropped created, size="+storedGPListCropped.size()+". Size of original: "+storedGPList.size());
		
		
		//time debug:
		/*int time=0;
		Log.w(TAG, storedGPListCropped.get(0).toString());
		for(int i=1; i<storedGPListCropped.size();i++){
			time+=storedGPListCropped.get(i).getTime()-storedGPListCropped.get(i-1).getTime();
			Log.w(TAG, storedGPListCropped.get(i).toString());			
		}
		Log.w(TAG, "storedGPListCropped total time: "+time); */
		//end time debug
		return storedGPListCropped;
	}
	
	protected StoredGeoPoint processLine(String line/*, long startTime, long endTime*/) {
		Scanner scanner = new Scanner(line);
		scanner.useDelimiter(",");
		try{
			if (scanner.hasNext()) {
				String dateStr = scanner.next();
				SimpleDateFormat sdfOld = new SimpleDateFormat(Preferences.DATE_WRITETOFILE_FORMAT);
		        Date date;
				try {
					date = sdfOld.parse(dateStr);
				} catch (ParseException e) {
					Log.e(TAG,"Wrong date in line (parsing failed): "+line, e);
					return null;
				}
		        long dateMs=date.getTime();
		        /*if(startTime>dateMs){ //if we haven't reached diapasone
		        	return null;
		        }
		        if(dateMs>=(endTime-1)){ //if diapasone already finished - special value
		        	return new StoredGeoPoint(0, 0,-1,0,0);
		        }*/
		        
				int lat = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				int lon = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				//int mode = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				float speed = Float.valueOf(scanner.next().replaceAll("\\s+", ""));
				//Log.i(TAG,"ROW parsed:" +date.toLocaleString()+" "+lat+" "+lon+" "+mode+" "+speed);
				StoredGeoPoint storedGeoData= new StoredGeoPoint(lat, lon,dateMs/*,mode*/,speed, StoredGeoPoint.POINT_TYPE_USUAL);
				return storedGeoData;
			} else {
				Log.e(TAG,"Empty or invalid line: "+line);
				return null;
			}
		} catch(NoSuchElementException ex){
			Log.w(TAG,"NoSuchElementException in line: "+line); //ex.getMessage() here ==null
			return null;
		} catch(IllegalStateException ex){
			Log.w(TAG,"IllegalStateException in line: "+line);
			return null;
		}
		// no need to call scanner.close(), since the source is a String
	}
	
/*
	protected StoredGeoPoint processFirstLine(String line, long startTime, long endTime) {
		Scanner scanner = new Scanner(line);
		scanner.useDelimiter(",");
		try{
			if (scanner.hasNext()) {
				String dateStr = scanner.next();
				SimpleDateFormat sdfOld = new SimpleDateFormat(Preferences.DATE_WRITETOFILE_FORMAT);
		        Date date;
				try {
					date = sdfOld.parse(dateStr);
				} catch (ParseException e) {
					Log.e(TAG,"Wrong date in line (parsing failed): "+line, e);
					return null;
				}
		        long dateMs=date.getTime();
		        if(startTime>dateMs){ //if we haven't reached diapasone
		        	return null;
		        }
		        
				int lat = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				int lon = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				//int mode = Integer.valueOf(scanner.next().replaceAll("\\s+", ""));
				float speed = Float.valueOf(scanner.next().replaceAll("\\s+", ""));
				StoredGeoPoint storedGeoPoint=null;
				if(dateMs>=(startTime+GeoDataProcessor.TIME_MS_ROBUST_SAME_TIME)){ //if video started earlier then coordinates been written - special value		        	
		        	storedGeoPoint= new StoredGeoPoint(lat, lon,dateMs,speed, StoredGeoPoint.POINT_TYPE_EMPTY);
		        }
				else{
					storedGeoPoint= new StoredGeoPoint(lat, lon,dateMs,speed, StoredGeoPoint.POINT_TYPE_USUAL);
				}
				return storedGeoPoint;
			} else {
				Log.e(TAG,"Empty or invalid line: "+line);
				return null;
			}
		} catch(NoSuchElementException ex){
			Log.w(TAG,"NoSuchElementException in line: "+line); //ex.getMessage() here ==null
			return null;
		} catch(IllegalStateException ex){
			Log.w(TAG,"IllegalStateException in line: "+line);
			return null;
		}
		// no need to call scanner.close(), since the source is a String
	}*/

}